package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumClientFeign;
import com.atguigu.tingshu.album.client.TrackClientFeign;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.IdWorker;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.payment.client.PaymentFeignClient;
import com.atguigu.tingshu.user.client.UserClientFeign;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.security.interfaces.RSAPrivateKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    private OrderInfoMapper orderInfoMapper;

    private UserClientFeign userClientFeign;

    private AlbumClientFeign albumClientFeign;

    private TrackClientFeign trackClientFeign;

    private RSAPrivateKey rsaPrivateKey;

    private OrderDetailMapper orderDetailMapper;

    private OrderDerateMapper orderDerateMapper;

    private RedisTemplate redisTemplate;

    private IdWorker idWorker;

    private RabbitTemplate rabbitTemplate;

    private RedissonClient redissonClient;

    private PaymentFeignClient paymentFeignClient;

    /**
     * 确认订单
     *
     * @param tradeVo 订单信息
     * @return 订单号
     */
    @Override
    public Object trade(TradeVo tradeVo) {
        // 获取订单的类型: 1001-专辑 1002-声音 1003-会员
        String itemType = tradeVo.getItemType();
        // 返回结果初始化
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        // 根据不同的类型生成不同的订单
        switch (itemType) {
            // 专辑订单
            case "1001" -> orderInfoVo = initAlbumOrder(tradeVo.getItemId());
            // 声音订单
            case "1002" -> orderInfoVo = initTrackOrder(tradeVo.getItemId(), tradeVo.getTrackCount());
            // 会员订单
            case "1003" -> orderInfoVo = initVipOrder(tradeVo.getItemId());
        }
        // 设置时间戳
        orderInfoVo.setTimestamp(System.currentTimeMillis());
        // 设置签名
        Jwt encode = JwtHelper.encode(JSONObject.toJSONString(orderInfoVo), new RsaSigner(rsaPrivateKey));
        orderInfoVo.setSign(encode.getEncoded());
        // 返回
        return orderInfoVo;
    }

    /**
     * 创建声音订单
     *
     * @param itemId     选中声音的id
     * @param trackCount 购买的声音数量
     * @return 订单的vo对象
     */
    private OrderInfoVo initTrackOrder(Long itemId, Integer trackCount) {
        // 创建订单vo对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        // 设置订单信息：交易号、付款项目类型
        // orderInfoVo.setTradeNo(UUID.randomUUID().toString().replace("-", ""));
        // 使用雪花算法替换UUID生成交易号
        orderInfoVo.setTradeNo("trackOrderNo-" + idWorker.nextId());
        orderInfoVo.setItemType("1002");

        // 查询可购买的声音信息
        JSONObject result = trackClientFeign.getTrackListAndPrice(itemId);

        // 获取声音单价
        BigDecimal price = new BigDecimal(result.get("price").toString());
        // 设置订单信息：原始金额、减免总金额、订单总金额
        orderInfoVo.setOriginalAmount(price.multiply(new BigDecimal(trackCount)));
        orderInfoVo.setDerateAmount(new BigDecimal(0));
        orderInfoVo.setOrderAmount(price.multiply(new BigDecimal(trackCount)));

        // 获取下单的声音列表: 1.泛型擦除 2.懒汉机制
        List<Object> list = (List) result.get("trackInfoList");
        // 过滤: 1.已购买 2.未支付
        Map<String, Object> userPaidTrackMap = userClientFeign.getUserPaidTrack(
                Long.valueOf(result.get("albumId").toString()),
                AuthContextHolder.getUserId()
        );
        Map<String, Object> userTrackOrderInfoMap = getUserTrackOrderInfo(AuthContextHolder.getUserId());

        List<OrderDetailVo> orderDetailVoList = list.stream()
                .map(o -> {
                    // 序列化
                    String s = JSONObject.toJSONString(o);
                    // 反序列化
                    return JSONObject.parseObject(s, TrackInfo.class);
                }).filter(f -> userPaidTrackMap.get(f.getId().toString()) == null &&
                        userTrackOrderInfoMap.get(f.getId().toString()) == null)
                .limit(trackCount)
                .map(trackInfo -> {
                    // 创建订单详情对象
                    OrderDetailVo orderDetailVo = new OrderDetailVo();
                    // 设置订单详情信息：声音id、声音单价、声音封面、声音标题
                    orderDetailVo.setItemId(trackInfo.getId());
                    orderDetailVo.setItemPrice(price);
                    orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                    orderDetailVo.setItemName(trackInfo.getTrackTitle());
                    // 返回
                    return orderDetailVo;
                }).collect(Collectors.toList());
        // 保存订单详情
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        // 保存减免详情
        orderInfoVo.setOrderDerateVoList(null);
        // 返回
        return orderInfoVo;
    }

    /**
     * 创建专辑订单: 条件-1.收费专辑 2.整张购买
     *
     * @param itemId 专辑的id
     * @return 订单的vo对象
     */
    private OrderInfoVo initAlbumOrder(Long itemId) {
        // 查询专辑信息
        AlbumInfo albumInfo = albumClientFeign.getAlbumInfo(itemId);

        //  如果专辑存在
        if (albumInfo != null && albumInfo.getId() != null) {
            // 专辑存在：获取专辑的支付类型和价格类型
            String payType = albumInfo.getPayType();
            String priceType = albumInfo.getPriceType();
            // 如果是收费专辑 并且是整张购买
            if (payType.equals("0103") && priceType.equals("0202")) {
                // 判断用户是否购买过该专辑：检查用户是否有未支付的订单
                if (orderInfoMapper.selectAlbumUnpaidOrderCount(AuthContextHolder.getUserId(), itemId) == 0) {
                    // 创建订单信息对象
                    OrderInfoVo orderInfoVo = new OrderInfoVo();
                    // 设置订单信息：交易号、付款项目类型
                    // orderInfoVo.setTradeNo(UUID.randomUUID().toString().replace("-", ""));
                    // 使用雪花算法替换UUID生成交易号
                    orderInfoVo.setTradeNo("albumOrderNo-" + idWorker.nextId());
                    orderInfoVo.setItemType("1001");
                    // 获取用户的vip状态 1-是 0-否 确定折扣率
                    Integer isVip = AuthContextHolder.getVip();
                    // 初始化折扣对象
                    BigDecimal dCount = null;
                    if (isVip.equals(1)) {
                        // 获取专辑的vip折扣
                        dCount = albumInfo.getVipDiscount();
                    } else {
                        // 获取专辑的普通折扣
                        dCount = albumInfo.getDiscount();
                    }
                    // 判断是否存在不打折的情况
                    dCount = dCount.compareTo(new BigDecimal(-1)) == 0 ?
                            new BigDecimal(1) : dCount.divide(new BigDecimal(10));
                    // 设置订单信息：原始金额、减免总金额、订单总金额
                    orderInfoVo.setOriginalAmount(albumInfo.getPrice());
                    orderInfoVo.setDerateAmount(albumInfo.getPrice().multiply(new BigDecimal(1).subtract(dCount)));
                    orderInfoVo.setOrderAmount(albumInfo.getPrice().multiply(dCount));

                    // 创建订单明细对象
                    OrderDetailVo orderDetailVo = new OrderDetailVo();
                    // 设置订单明细信息：付费项目id、项目名称、项目图片、项目价格
                    orderDetailVo.setItemId(itemId);
                    orderDetailVo.setItemName("买专辑:" + albumInfo.getAlbumTitle());
                    orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
                    orderDetailVo.setItemPrice(orderInfoVo.getOrderAmount());
                    // 将订单明细添加到订单明细列表
                    List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
                    orderDetailVoList.add(orderDetailVo);
                    orderInfoVo.setOrderDetailVoList(orderDetailVoList);

                    // 订单的减免详情
                    OrderDerateVo orderDerateVo = new OrderDerateVo();
                    // 设置订单减免详情信息：减免类型、减免金额、备注
                    orderDerateVo.setDerateType("1405");
                    orderDerateVo.setDerateAmount(orderInfoVo.getDerateAmount());
                    orderDerateVo.setRemarks("专辑折扣");
                    // 将订单减免详情添加到订单减免详情列表
                    List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
                    orderDerateVoList.add(orderDerateVo);
                    orderInfoVo.setOrderDerateVoList(orderDerateVoList);
                    // 返回
                    return orderInfoVo;
                }
            }
        }
        throw new GuiguException(201, "专辑不能直接购买");
    }

    /**
     * 创建vip订单
     *
     * @param itemId vip的id
     * @return 订单的vo对象
     */
    private OrderInfoVo initVipOrder(Long itemId) {

        // 获取本次购买的vip详细信息: 1月 3月 1年
        VipServiceConfig vipServiceConfig = userClientFeign.getVipServiceConfig(itemId);

        // 创建订单VO对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        // 设置订单信息：交易号、付款项目类型、原始金额、减免总金额、订单总金额
        // orderInfoVo.setTradeNo(UUID.randomUUID().toString().replace("-", ""));
        // 使用雪花算法替换UUID生成交易号
        orderInfoVo.setTradeNo("vipOrderNo-" + idWorker.nextId());
        orderInfoVo.setItemType("1003");
        orderInfoVo.setOriginalAmount(vipServiceConfig.getPrice());
        orderInfoVo.setDerateAmount(vipServiceConfig.getPrice().subtract(vipServiceConfig.getDiscountPrice()));
        orderInfoVo.setOrderAmount(vipServiceConfig.getDiscountPrice());

        // 创建订单明细对象
        OrderDetailVo orderDetailVo = new OrderDetailVo();
        // 设置订单明细信息：付费项目id、项目名称、项目图片、项目价格
        orderDetailVo.setItemId(itemId);
        orderDetailVo.setItemName("买VIP:" + vipServiceConfig.getName());
        orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
        orderDetailVo.setItemPrice(vipServiceConfig.getDiscountPrice());
        // 将订单明细添加到订单明细列表
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        orderDetailVoList.add(orderDetailVo);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);

        // 创建订单减免详情对象
        OrderDerateVo orderDerateVo = new OrderDerateVo();
        // 设置订单减免详情信息：减免类型、减免金额、备注
        orderDerateVo.setDerateType("1406");
        orderDerateVo.setDerateAmount(orderInfoVo.getDerateAmount());
        orderDerateVo.setRemarks("VIP服务折扣!");
        // 将订单减免详情添加到订单减免详情列表
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        orderDerateVoList.add(orderDerateVo);
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        // 返回
        return orderInfoVo;
    }

    /**
     * 查询用户准备购买的声音列表
     *
     * @param userId 用户id
     * @return 声音列表
     */
    @Override
    public Map<String, Object> getUserTrackOrderInfo(Long userId) {
        // 查询用户未支付的声音订单的全部声音列表
        List<Long> trackIdList = orderInfoMapper.selectTrackIdList(userId);
        // 转为map返回
        return trackIdList.stream().collect(Collectors.toMap(
                key -> key.toString(),
                value -> 0
        ));
    }

    /**
     * 提交订单
     *
     * @param orderInfoVo 订单信息
     * @return 订单号
     */
    @Override
    public Object submitOrder(OrderInfoVo orderInfoVo) {

        // 获取签名
        String sign = orderInfoVo.getSign();

        // 校验签名
        if (StringUtils.isEmpty(sign)) {
            throw new GuiguException(201, "签名错误");
        }
        try {
            // 校验签名
            Jwt jwt = JwtHelper.decodeAndVerify(sign, new RsaVerifier(SystemConstant.PUBLIC_KEY));
            // 获取载荷
            String claims = jwt.getClaims();
            // 反序列化 覆盖原来的orderInfoVo 保证数据安全
            orderInfoVo = JSONObject.parseObject(claims, OrderInfoVo.class);
            // 校验时间 5分钟到期
            if (System.currentTimeMillis() - orderInfoVo.getTimestamp() > 5 * 60 * 1000) {
                throw new GuiguException(201, "停留时间过长，订单已过期，请返回重新下单");
            }

            // 校验订单是否重复：A:限制类型
            // if (!checkOrderRepeatA(orderInfoVo)) {
            //     throw new GuiguException(201, "订单重复提交，请勿重复提交订单");
            // }

            // 校验订单是否重复：B:限制内容
            if (!checkOrderRepeatB(orderInfoVo)) {
                throw new GuiguException(201, "订单重复提交，请勿重复提交订单");
            }

            // 创建订单信息对象
            OrderInfo orderInfo = new OrderInfo();
            // 设置订单信息
            BeanUtils.copyProperties(orderInfoVo, orderInfo);
            orderInfo.setUserId(AuthContextHolder.getUserId());
            // 获取订单类型 根据不同的订单类型保存不同的订单信息
            String itemType = orderInfoVo.getItemType();
            switch (itemType) {
                case "1001" -> orderInfo.setOrderTitle("专辑订单");
                case "1002" -> orderInfo.setOrderTitle("声音订单");
                case "1003" -> orderInfo.setOrderTitle("会员订单");
            }
            orderInfo.setOrderNo(orderInfoVo.getTradeNo());
            orderInfo.setOrderStatus("0901");

            // 保存到数据库
            if (save(orderInfo)) {
                // 获取订单id
                Long orderId = orderInfo.getId();
                // 保存订单详情信息
                saveOrderDetail(orderInfoVo.getOrderDetailVoList(), orderId);
                // 保存订单减免信息
                saveOrderDerate(orderInfoVo.getOrderDerateVoList(), orderId);
                // 返回
                JSONObject result = new JSONObject();
                result.put("orderId", orderInfo.getOrderNo());

                // 开始订单的支付倒计时
                rabbitTemplate.convertAndSend("order_delay_exchange", "order.delay.dead",
                        orderInfo.getOrderNo() + ":" + orderInfo.getUserId(),
                        message -> {
                            // 获取消息的属性
                            MessageProperties messageProperties = message.getMessageProperties();
                            // 设置过期时间
                            messageProperties.setExpiration(600000 + "");
                            // 返回
                            return message;
                        }
                );
                // 返回
                return result;
            }
        } catch (Exception e) {
            // 立即放锁 操作异常释放锁
            releaseLock(orderInfoVo.getOrderDetailVoList().get(0).getItemId(),
                    orderInfoVo.getItemType(),
                    AuthContextHolder.getUserId());
            throw e;
        }
        return null;
    }

    /**
     * 取消订单：超时取消/主动取消
     *
     * @param orderNo 订单号
     */
    @SneakyThrows
    @Override
    public void cancelOrder(String orderNo) {
        // 默认主动取消 0903-已取消 0904-已过期
        String status = "0903";
        // 判断订单是超时取消还是主动取消
        Long userId = AuthContextHolder.getUserId();
        if (userId == null) {
            // 超时
            status = "0904";
            // 拆分订单号
            String[] split = orderNo.split(":");
            // 获取用户id
            userId = Long.valueOf(split[1]);
            // 获取订单号
            orderNo = split[0];
        }
        // 抢锁: userId+orderNo
        RLock lock = redissonClient.getLock("OrderCancel_" + userId + "_" + "OrderNo_" + orderNo);
        if (lock.tryLock()) {
            try {
                // 取消订单:单线程
                OrderInfo orderInfo = getOne(new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getOrderNo, orderNo)
                        .eq(OrderInfo::getUserId, userId)
                        .eq(OrderInfo::getOrderStatus, "0901")
                );
                if (orderInfo == null) {
                    return;
                }
                // 修改订单状态
                orderInfo.setOrderStatus(status);
                if (!updateById(orderInfo)) {
                    throw new GuiguException(201, "取消订单失败，请重试！");
                }
                // 查询itemId
                Long itemId = orderDetailMapper.selectItemIdByOrderId(orderInfo.getId());
                // 清理redis中下单时放进去的锁标识 不影响用户下新单
                releaseLock(itemId, orderInfo.getItemType(), userId);
            } catch (Exception e) {
                throw e;
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * 释放锁
     *
     * @param itemId   订单id
     * @param itemType 订单类型
     */
    private void releaseLock(Long itemId, String itemType, Long userId) {
        // 获取详情
        if (itemType.equals("1002")) {
            // 查询专辑的数据获取专辑id
            itemId = trackClientFeign.getTrackInfoDetail(itemId).getAlbumId();
        }
        // 自动释放锁
        redisTemplate.expire("SubmitOrder_" + userId + "_" + itemType + "_" + itemId, 5, TimeUnit.SECONDS);
        // 手动释放锁
        redisTemplate.delete("SubmitOrder_" + userId + "_" + itemType + "_" + itemId);
    }

    /**
     * 校验订单是否重复:A限制类型-同一用户只允许存在同一类型的一个未支付订单
     *
     * @param orderInfoVo 订单信息

     @SneakyThrows private Boolean checkOrderRepeatA(OrderInfoVo orderInfoVo) {

     // 获取用户id
     Long userId = AuthContextHolder.getUserId();
     // 获取订单类型
     String itemType = orderInfoVo.getItemType();
     // 加分布式锁
     if (redisTemplate.opsForValue().setIfAbsent("SubmitOrder_" + userId + "_" + itemType,
     0,
     10,
     TimeUnit.MINUTES)) {
     return true;
     }
     return false;
     }
     */

    /**
     * 限制内容: 同一个用户对同一个内容只能生成一个未支付的订单
     * 会员：锁定用户 + 会员的种类
     * 专辑：锁定用户 + 专辑的id
     * 声音：锁定用户 + 声音的id 同一专辑、同一用户只允许存在一个未支付的声音订单
     *
     * @param orderInfoVo 订单信息
     */
    @SneakyThrows
    private Boolean checkOrderRepeatB(OrderInfoVo orderInfoVo) {
        // 获取用户id
        Long userId = AuthContextHolder.getUserId();
        // 获取详情
        Long itemId = orderInfoVo.getOrderDetailVoList().get(0).getItemId();
        // 获取订单类型 1001-专辑 1002-声音 1003-会员
        String itemType = orderInfoVo.getItemType();
        if ("1002".equals(itemType)) {
            // 获取声音id
            itemId = trackClientFeign.getTrackInfoDetail(itemId).getAlbumId();
        }
        // 加分布式锁
        if (redisTemplate.opsForValue().setIfAbsent("SubmitOrder_" + userId + "_" + itemType + "_" + itemId,
                0,
                10,
                TimeUnit.MINUTES)) {
            return true;
        }
        return false;
    }

    /**
     * 保存订单减免信息
     *
     * @param orderDerateVoList 订单减免信息
     * @param orderId           订单id
     */
    private void saveOrderDerate(List<OrderDerateVo> orderDerateVoList, Long orderId) {

        // 判断是否存在订单减免信息:声音订单没有减免信息
        if (orderDerateVoList == null || orderDerateVoList.size() == 0) {
            return;
        }
        // 遍历订单减免信息
        orderDerateVoList.stream().forEach(orderDerateVo -> {
            // 创建订单减免对象
            OrderDerate orderDerate = new OrderDerate();
            // 设置订单减免信息
            orderDerate.setOrderId(orderId);
            orderDerate.setDerateType(orderDerateVo.getDerateType());
            orderDerate.setDerateAmount(orderDerateVo.getDerateAmount());
            orderDerate.setRemarks(orderDerateVo.getRemarks());
            // 保存
            int insert = orderDerateMapper.insert(orderDerate);
            if (insert <= 0) {
                throw new GuiguException(201, "订单减免信息保存失败");
            }
        });
    }

    /**
     * 保存订单详情信息
     *
     * @param orderDetailVoList 订单详情信息
     * @param orderId           订单id
     */
    private void saveOrderDetail(List<OrderDetailVo> orderDetailVoList, Long orderId) {

        // 遍历订单详情信息
        orderDetailVoList.stream().forEach(orderDetailVo -> {
            // 创建订单详情对象
            OrderDetail orderDetail = new OrderDetail();
            // 设置订单详情信息
            orderDetail.setOrderId(orderId);
            orderDetail.setItemId(orderDetailVo.getItemId());
            orderDetail.setItemPrice(orderDetailVo.getItemPrice());
            orderDetail.setItemUrl(orderDetailVo.getItemUrl());
            orderDetail.setItemName(orderDetailVo.getItemName());
            // 保存
            int insert = orderDetailMapper.insert(orderDetail);
            if (insert <= 0) {
                throw new GuiguException(201, "订单详情保存失败");
            }
        });
    }

    /**
     * 分页条件查询指定用户的订单列表
     *
     * @param page 当前页
     * @param size 每页显示的记录数
     * @return 订单列表
     */
    @Override
    public Object findUserPage(Integer page, Integer size) {
        // 查询指定用户的全部订单的信息
        return orderInfoMapper.selectUserOrderPage(
                new Page<OrderInfo>(page, size),
                AuthContextHolder.getUserId());
    }

    /**
     * 查询订单的详情
     *
     * @param orderNo 订单号
     * @return 订单的详细信息
     */
    @Override
    public Object getOrderInfo(String orderNo) {
        // 查询订单
        OrderInfo orderInfo = getOne(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getOrderNo, orderNo)
                .eq(OrderInfo::getUserId, AuthContextHolder.getUserId()));
        // 查询详情
        List<OrderDetail> orderDetailList =
                orderDetailMapper.selectList(
                        new LambdaQueryWrapper<OrderDetail>()
                                .eq(OrderDetail::getOrderId, orderInfo.getId())
                );
        // 保存详情
        orderInfo.setOrderDetailList(orderDetailList);
        // 查询减免详情
        List<OrderDerate> orderDerateList = orderDerateMapper.selectList(
                new LambdaQueryWrapper<OrderDerate>()
                        .eq(OrderDerate::getOrderId, orderInfo.getId()));
        orderInfo.setOrderDerateList(orderDerateList);
        // 返回
        return orderInfo;
    }

    /**
     * 获取订单支付信息（根据用户选择的支付方式）
     *
     * @param orderNo 订单号
     * @param payWay  支付方式
     * @return 订单支付信息
     */
    @Override
    public Object getOrderPayInfo(String orderNo, String payWay) {
        // 校验参数
        if (StringUtils.isEmpty(orderNo) || StringUtils.isEmpty(payWay)) {
            throw new GuiguException(201, "参数错误");
        }

        // 获取用户id
        Long userId = AuthContextHolder.getUserId();

        // redis查询支付信息
        String codeUrl = (String) redisTemplate.opsForValue().get("Order_PayInfo_User_" + userId + "_OrderNo_" + orderNo + "_Payway" + payWay);

        if (StringUtils.isNotEmpty(codeUrl)) {
            return codeUrl;
        }
        // 解决重复申请: 时长限制10min-同一个人同一个订单10min内只能有一个支付地址
        RLock lock = redissonClient.getLock("Order_PayInfo_Lock_User_" + userId + "_OrderNo_" + orderNo);
        // 加锁
        if (lock.tryLock()) {
            try {
                // 查询订单信息
                OrderInfo orderInfo = getOne(new LambdaQueryWrapper<OrderInfo>()
                        .eq(OrderInfo::getOrderNo, orderNo)
                        .eq(OrderInfo::getUserId, AuthContextHolder.getUserId())
                        .eq(OrderInfo::getOrderStatus, "0901"));

                // 判断订单是否存在
                if (orderInfo == null) {
                    throw new GuiguException(201, "订单不存在或已支付");
                }

                // 订单存在 根据用户选择的支付方式到支付微服务申请支付 并返回支付地址
                switch (payWay) {
                    case "1101" -> codeUrl = paymentFeignClient.getPayInfoFromWx(
                            orderInfo.getOrderNo(),
                            orderInfo.getOrderAmount().toString(),
                            orderInfo.getOrderTitle());
                    case "1102" -> codeUrl = "支付宝";
                    // TODO--待完成
                    case "1103" -> codeUrl = "余额";
                }

                // redis存储:10分钟
                if (codeUrl != null) {
                    redisTemplate.opsForValue().set(
                            "Order_PayInfo_User_" + userId + "_OrderNo_" + orderNo + "_Payway" + payWay,
                            codeUrl,
                            10,
                            TimeUnit.MINUTES);
                }
            } catch (Exception e) {
                throw e;
            } finally {
                lock.unlock();
            }
        }
        return codeUrl;
    }
}